package productions;

import exceptions.ParserException;
import lexico.ErrorTable;
import lexico.SymbolTable;
import lexico.TokenClass;

/**
 * @author heekinho
 */
public class ParaProduction extends AbstractProductionRule {
    /* Muitos dos métodos irão precisar de algo assim: */

    private static ParaProduction instance = new ParaProduction();

    public static ParaProduction getInstance() {
        return instance;
    }


    /**
     * Regra: algoritmo: para : 'para' '(' (termo_para (',' termo_para)*)? ';' expr? ';' (expr (',' expr)*)? ')' bloco;
     * @return
     * @throws ParserException
     */
    @Override
    public boolean analyze(SymbolTable st, ErrorTable et) throws ParserException {
        if (!basicParse(st, TokenClass.KEYWORD_PARA)) {
            return false;
        }

        if (!basicParse(st, TokenClass.DELIMITER_OPENING_PARENTHESIS)) {
            return skipUntilSynchronize(st, et, "Era esperado um '('");
        }

        if (TipoVariavelProduction.getInstance().first.contains(st.getCurrent().getTokenClass())) {
            if (!termoPara(st, et)) {
                return skipUntilSynchronize(st, et, "Era esperado uma expressão.");
            }

            while (basicParse(st, TokenClass.DELIMITER_COMMA)) {
                if (!termoPara(st, et)) {
                    return skipUntilSynchronize(st, et, "Era esperado uma expressão.");
                }
            }
        }

        if (!basicParse(st, TokenClass.DELIMITER_SEMICOLON)) {
            return skipUntilSynchronize(st, et, "Era esperado um ';'.");
        }
        ExprProduction.getInstance().analyze(st, et);
        if (!basicParse(st, TokenClass.DELIMITER_SEMICOLON)) {
            return skipUntilSynchronize(st, et, "Era esperado um ';'.");
        }

        if (ExprProduction.getInstance().analyze(st, et)) {
            while (basicParse(st, TokenClass.DELIMITER_COMMA)) {
                if (!ExprProduction.getInstance().analyze(st, et)) {
                    return skipUntilSynchronize(st, et, "Era esperado um identificador.");
                }
            }
        }

        if (!basicParse(st, TokenClass.DELIMITER_CLOSING_PARENTHESIS)) {
            return skipUntilSynchronize(st, et, "Era esperado um ')'");
        }
        if (!BlocoProduction.getInstance().analyze(st, et)) {
            return skipUntilSynchronize(st, et, "Era esperado um '{'");
        }
        return true;
    }

    /**
     * Regra: termo_para : (tipo_primitivo IDENTIFIER | IDENTIFIER IDENTIFIER?) array*  ('=' valor)?;
     * @return
     * @throws ParserException
     */
    private boolean termoPara(SymbolTable st, ErrorTable et) throws ParserException {
        if (TipoPrimitivoProduction.getInstance().analyze(st, et)) {
            if (!basicParse(st, TokenClass.IDENTIFIER)) {
                return skipUntilSynchronize(st, et, "Era esperado um identificador");
            }
        } else if (basicTest(st, TokenClass.IDENTIFIER)) {
            basicParse(st, TokenClass.IDENTIFIER);
        } else {
            return false;
        }

        while (ArrayProduction.getInstance().analyze(st, et)) {
        }

        if (basicParse(st, TokenClass.OPERATOR_ASSIGNMENT)) {
            if (!ValorProduction.getInstance().analyze(st, et)) {
                return skipUntilSynchronize(st, et, "Era esperado uma atribuição");
            }
        }

        return true;
    }

    @Override
    protected void populateFirst() {
        first.add(TokenClass.KEYWORD_PARA);
    }

    @Override
    protected void populateFollow() {
        //from comandos follow
        follow.add(TokenClass.DELIMITER_CLOSING_CURLY_BRACKET);
    }
}
